package com.hanj.spring;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * BeanFactory 的实现类,用来真正创建容器和从容器中获取 bean
 *
 * @author hanj.cn@outlook.com
 */
public class ClassPathXmlApplicationContext implements BeanFactory {

    // 可以理解为容器,这个 map 的键值对形式是: beanId-className
    private final Map<String, Object> container = new HashMap<>();

    public ClassPathXmlApplicationContext(String filePath) throws Exception {

        // 创建构建器
        SAXBuilder sb = new SAXBuilder();
        // 构造文档对象
        Document doc = sb.build(this.getClass().getClassLoader().getResourceAsStream(filePath));
        // 获取根元素
        Element root = doc.getRootElement();
        // 获取名为 bean 的子元素
        List<Element> list = root.getChildren("bean");

        System.out.println("------------------------------------------------------------------");

        for (Element element : list) {
            String beanId = element.getAttributeValue("id");
            String className = element.getAttributeValue("class");

            // 通过反射获取对象实例
            Object instance = Class.forName(className).newInstance();

            System.out.printf("容器中添加一对 [%s - %s]\n", beanId, className);

            // 容器中放入一个 beanId - bean实例 (未初始化)
            container.put(beanId, instance);

            // 在这个循环里为每个实例初始化(注入属性)
            for (Element propertyElement : element.getChildren("property")) {
                beanId = propertyElement.getAttributeValue("bean");

                // 从上一步放入容器内的对象实例中寻找 bean,然后将其注入
                Object bean = container.get(beanId);

                // 这里的 setUserDao 是写死的
                String methodName = "setUserDao";
                // 获取相应的 set 方法,注意 "bean.getClass().getInterfaces()[0]" 这样写的原因是 UserService 中的 userDao 属性是接口 "UserDao"
                Method method = instance.getClass().getMethod(methodName, bean.getClass().getInterfaces()[0]);
                // 执行注入
                method.invoke(instance, bean);
            }
        }
        System.out.println("------------------------------------------------------------------");
    }

    @Override
    public Object getBean(String beanId) {

        return container.get(beanId);
    }

}
